package common;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;



public class CMultinomialProbabiliteAPosteriori extends CMultinomial{


	protected FileWriter FichierEstimationPolarite;
	protected boolean afficherQuePrediction;



	/**
	 * Une instance de la classe ModeleMultinomial permet de créer un modèle, en se basant
	 * sur les informations contenues dans un fichier d'apprentissage "FicApp", pour pouvoir
	 * essayer de classifier les critiques contenues dans le fichier "FicTest".
	 * @param FicApp	le fichier d'apprentissage, contenant des critiques labellisées
	 * @param FicTest	le fichier de test, contenant des critiques dont la classe
	 * (positive ou negative) est a prédire
	 * @param suppression	permet d'enlever les mots outils (true) ou pas (false)
	 * @param seuil			correspond au seuil de fréquence maximal au-dela duquel les mots
	 * 						du vocabulaire initial ne seront pas retenus
	 * @param epsilon		constante de Laplace que l'on ajoute au numérateur, dans le calcul
	 * 						du Béta(Mot,Classe) pour éviter d'avoir des paramètres Béta
	 * 						égaux à 0. (epsilon > 1)
	 */
	public CMultinomialProbabiliteAPosteriori(ArrayList<File>  fichiersAp, File FicTest, String outputName, boolean suppression,
			double seuil, double epsilon, boolean formatterQ3){
		super();

		try {
			FichierEstimationPolarite = new FileWriter(outputName);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// ------------------------------------------
		// Initialisation des paramètres pouvant influer l'estimation du jeu de test
		// ------------------------------------------

		SUPPRESSIONMOTSOUTILS = suppression;
		SEUIL_FREQUENCE_MAXI = seuil;
		EPSILON = epsilon;

		/**
		 * Initialisation de l'ensemble des variables utilisées
		 */

		listeFichiersApprentissage = fichiersAp;
		FichierTest = FicTest;
		afficherQuePrediction = formatterQ3;

		for(File f : listeFichiersApprentissage){
			System.out.println("(Classifieur étiquettes floues) Fichier apprentissage :" + f.getAbsolutePath());
		}
		System.out.println("(Classifieur étiquettes floues) Fichier Test : " + FichierTest.getAbsolutePath());



		listeMotsOutils = new ArrayList<String>();
		dictionnaire = new TreeMap<String, Integer>();

		motCategorieNbOcc = new TreeMap<String, Map<String,Double>>();

		dictionnaireFiltre = new TreeMap<String, Integer>();

		motCategorieNbOccFiltre = new TreeMap<String, Map<String,Double>>();

		categorieNbMotsDansToutesLesCritiquesDeLaCategorie = new TreeMap<String, Double>();

		motCategorieParametre = new TreeMap<String, Map<String,Double>>();

		nbCritiques = 0;
		nbMotsDuCorpus = 0;

		nbCritiquesPositives = 0;
		nbCritiquesNegatives = 0;


		// ------------------------------------------
		// Début de l'apprentissage
		// ------------------------------------------

		if (SUPPRESSIONMOTSOUTILS) suppressionMotsOutils();
		creationDictionnaire();

		// Affichage de quelques statistiques (le nombre de mots
		// peut varier selon les valeurs des paramètres considérés)
		/*System.out.println("Nb Critiques : " + nbCritiques);
		System.out.println("Nb positives : " + nbCritiquesPositives);
		System.out.println("Nb negatives: " + nbCritiquesNegatives);
		System.out.println("Nb Mots Du corpus : " + nbMotsDuCorpus);

		System.out.println("Nb mots outils dans cette configuration : " + listeMotsOutils.size());*/

		System.out.println("Création du corpus avec étiquettes floues ...");

		filtrageDictionnaireSeuilMaximum();

		//System.out.println("Taille dictionnaire filtré : " + dictionnaireFiltre.size());

		sauverDictionnaire();
		sauverMotsPlusFrequentsDictionnaire();
		sauverMotCategorieNbDocs();

		creerEtSauvegarderMotCategorieParametre();


		// ------------------------------------------
		// 2ème étape (et 3ème étape) : détermination de la polarité des critiques, 
		// par rapport é un jeu d'apprentissage
		// ------------------------------------------

		classification();

	}



	public void sauverMotCategorieNbDocs(){
		System.out.println("Sauve MotCategorieNbDocs");
		// Avant de sauver le map MotCategorieNbDocs, on va gérer le cas oé un mot n'apparait dans
		// aucun document

		for(String mot : motCategorieNbOccFiltre.keySet()){
			if(!motCategorieNbOccFiltre.get(mot).containsKey("positive")){
				//motCategorieNbOccFiltre.get(mot).put("positive",K);
				motCategorieNbOccFiltre.get(mot).put("positive",0.0);
			}
			if(!motCategorieNbOccFiltre.get(mot).containsKey("negative")){
				//motCategorieNbOccFiltre.get(mot).put("negative",K);
				motCategorieNbOccFiltre.get(mot).put("negative",0.0);
			}
		}

		// Ecriture dans un fichier de sortie
		PrintWriter ecrivain = null;

		try {
			ecrivain =  new PrintWriter(new BufferedWriter
					(new FileWriter("MotCategorieNbDocs.txt")));
		} catch (IOException e) {

			e.printStackTrace();
		}

		for(String mot : motCategorieNbOccFiltre.keySet()){
			ecrivain.print(mot);
			for(String categorie : motCategorieNbOccFiltre.get(mot).keySet()){
				ecrivain.println("\t" + categorie + "----->" + motCategorieNbOccFiltre.get(mot).get(categorie));
			}
		}
		ecrivain.close();
	}

	/**
	 * Cette fonction permet, à partir du nombre d'occurences de chaque mot dans
	 * chaque classe (motCategorieNbOccFiltre) et du nombre d'occurences de TOUS les mots
	 * dans chaque classe (categorieNbMotsDansToutesLesCritiquesDeLaCategorie), de déduire
	 * le paramètre Béta(mot, classe), correspondant à : 
	 * Béta(Mot,Classe) = (Nb Occurences du mot dans tous les documents de la classe) + EPSILON
	 * 															
	 * 															/
	 * 								
	 * 					(Nb Occurences de TOUS les mots de tous les documents de la classe) + EPSILON * Nombre de mots
	 * 
	 * Ici, on rajoute EPSILON au numérateur, pour éviter d'avoir des paramètres nuls. On dit
	 * qu'on utilise le lissage de Laplace.
	 * 
	 */
	public void creerEtSauvegarderMotCategorieParametre() {

		System.out.println("Sauve motCategorieParametre");

		// Calcul du nombre total de mots dans tous les documents SPAM
		categorieNbMotsDansToutesLesCritiquesDeLaCategorie.put("positive", 0.0);
		for(String mot : motCategorieNbOccFiltre.keySet()){
			categorieNbMotsDansToutesLesCritiquesDeLaCategorie.put
			("positive", categorieNbMotsDansToutesLesCritiquesDeLaCategorie.get("positive") 
					+ motCategorieNbOccFiltre.get(mot).get("positive"));
		}
		// Calcul du nombre total de mots dans tous les documents NONSPAM
		categorieNbMotsDansToutesLesCritiquesDeLaCategorie.put("negative", 0.0);
		for(String mot : motCategorieNbOccFiltre.keySet()){
			categorieNbMotsDansToutesLesCritiquesDeLaCategorie.put
			("negative", categorieNbMotsDansToutesLesCritiquesDeLaCategorie.get("negative") 
					+ motCategorieNbOccFiltre.get(mot).get("negative"));
		}

		// Calcul de chaque paramètre pour chaque mot, chaque catégorie
		for(String mot : motCategorieNbOccFiltre.keySet()){

			for(String categorie : motCategorieNbOccFiltre.get(mot).keySet()){

				if(!motCategorieParametre.containsKey(mot)){
					TreeMap<String, Double> categorieParametre = new TreeMap<String, Double>();
					categorieParametre.put(categorie,  (motCategorieNbOccFiltre.get(mot).get(categorie) + EPSILON) / (categorieNbMotsDansToutesLesCritiquesDeLaCategorie.get(categorie) + EPSILON*dictionnaireFiltre.size()));
					motCategorieParametre.put(mot, categorieParametre);
				}
				else {
					motCategorieParametre.get(mot).put(categorie,(motCategorieNbOccFiltre.get(mot).get(categorie) + EPSILON) / (categorieNbMotsDansToutesLesCritiquesDeLaCategorie.get(categorie) + EPSILON*dictionnaireFiltre.size()));
				}
			}
		}
	}

	public void classification() {


		ArrayList<Critique> lesCritiquesAEvaluer = determinerLesCritiques(FichierTest);

		PrintWriter ecrivain_justlab =  new PrintWriter(new BufferedWriter(FichierEstimationPolarite));

		double logVraiSemblancePositive;
		double logVraiSemblanceNegative;

		System.out.println("Nombre critiques du fichier test : " + lesCritiquesAEvaluer.size());
		for(int i = 0; i < lesCritiquesAEvaluer.size(); i++){


			logVraiSemblancePositive = logVraisemblance(lesCritiquesAEvaluer.get(i).getContenu(), "positive");
			logVraiSemblanceNegative = logVraisemblance(lesCritiquesAEvaluer.get(i).getContenu(), "negative");

			// Une fois que l'on a déterminé la logVraiSemblancePositive et négative
			// pour la critique courante, on va écrire un fichier résultat
			// nommé "justlab.txt", qui va contenir pour chaque critique la probabilité
			// que celle ci soit positive ou négative, selon la valeur de log vraisemblance
			// qui est supérieure à l'autre.

			// On prend la valeur absolue de chaque log, une fois qu'on a déterminé
			// quelle logVraisemblance est la plus grande, pour avoir bien des valeurs
			// supérieures à 0.5 dans le fichier de sortie "justlab_part2_3.txt"
			if (afficherQuePrediction){
				
				if(logVraiSemblancePositive > logVraiSemblanceNegative){
					ecrivain_justlab.println("<review id='"+ 
							(lesCritiquesAEvaluer.get(i).getIdentifiant()-1) + "' class='positive'>");								
				}
				else {
					ecrivain_justlab.println("<review id='"+ 
							(lesCritiquesAEvaluer.get(i).getIdentifiant()-1) + "' class='negative'>");
				}
			}
			else {

				if(logVraiSemblancePositive > logVraiSemblanceNegative){
					ecrivain_justlab.println("<review id='"+ 
							lesCritiquesAEvaluer.get(i).getIdentifiant() + "' class='positive:"
							+ (1 - (logVraiSemblancePositive / 
									(logVraiSemblancePositive + logVraiSemblanceNegative))) + "'>");
					ecrivain_justlab.println(lesCritiquesAEvaluer.get(i).getContenu());
					ecrivain_justlab.println("</review>\n");

				}
				else {
					ecrivain_justlab.println("<review id='"+ 
							lesCritiquesAEvaluer.get(i).getIdentifiant() + "' class='negative:"
							+ (1 - (logVraiSemblanceNegative / 
									(logVraiSemblancePositive + logVraiSemblanceNegative))) + "'>");
					ecrivain_justlab.println(lesCritiquesAEvaluer.get(i).getContenu());
					ecrivain_justlab.println("</review>\n");
				}
			}
		}	
		ecrivain_justlab.close();
	}

}
